package com.bestcem.xm.survey.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.bestcem.xm.common.core.exception.BusinessException;
import com.bestcem.xm.componet.redis.service.RedisService;
import com.bestcem.xm.qdes.grpc.v1.services.Project;
import com.bestcem.xm.qdes.grpc.v1.services.QStruct;
import com.bestcem.xm.survey.controller.vo.SurveyColumnVo;
import com.bestcem.xm.survey.convert.grpc.SurveyGrpcConvertMapper;
import com.bestcem.xm.survey.entity.*;
import com.bestcem.xm.survey.grpc.client.DeliverGrpcClient;
import com.bestcem.xm.survey.grpc.client.QdesGrpcClient;
import com.bestcem.xm.survey.grpc.client.UserGrpcClient;
import com.bestcem.xm.survey.grpc.client.dto.DeliverExtParamsDto;
import com.bestcem.xm.survey.grpc.client.dto.QstructsDto;
import com.bestcem.xm.survey.grpc.v1.services.ColumnCtype;
import com.bestcem.xm.survey.mapper.SurveyColumnMapper;
import com.bestcem.xm.survey.mapper.SurveyRespondentDataMapper;
import com.bestcem.xm.survey.mapper.SurveyRespondentMapper;
import com.bestcem.xm.survey.mq.dto.CreateColumnsDto;
import com.bestcem.xm.survey.mq.dto.CreateQuotaDto;
import com.bestcem.xm.survey.mq.dto.TicketNotificationTriggerDto;
import com.bestcem.xm.survey.mq.send.biz.SurveyMessageSendService;
import com.bestcem.xm.survey.service.*;
import com.google.common.collect.Lists;
import lombok.NonNull;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import org.springframework.util.ObjectUtils;

import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.BiFunction;

import static com.bestcem.xm.survey.grpc.v1.services.ColumnCtype.*;
import static com.bestcem.xm.survey.grpc.v1.services.ColumnQtype.*;
import static com.bestcem.xm.survey.grpc.v1.services.ColumnStatus.*;
import static com.bestcem.xm.survey.grpc.v1.services.RespondentDataStatus.RESPONDENT_DATA_STATUS_FORMAL_VALUE;
import static com.bestcem.xm.survey.grpc.v1.services.RespondentStatus.RESPONDENT_STATUS_FINISH_VALUE;
import static com.bestcem.xm.survey.util.SurveyConstant.*;

@Slf4j
@Service
@RequiredArgsConstructor
public class SurveyMessageReceiverServiceImpl implements SurveyMessageReceiverService {

    private @NonNull RedisService redisService;
    private @NonNull SurveyCommonService surveyCommonService;
    private @NonNull SurveyExtParameterService surveyExtParameterService;
    private @NonNull SurveyQuotaCheckItemService surveyQuotaCheckItemService;
    private @NonNull SurveyMessageSendService surveyMessageSendService;
    private @NonNull SurveyColumnService surveyColumnService;

    private @NonNull SurveyColumnMapper columnMapper;
    private @NonNull SurveyRespondentMapper surveyRespondentMapper;
    private @NonNull SurveyRespondentDataMapper surveyRespondentDataMapper;

    private @NonNull UserGrpcClient userGrpcClient;
    private @NonNull QdesGrpcClient qdesGrpcClient;
    private @NonNull DeliverGrpcClient deliverGrpcClient;

    private final SurveyGrpcConvertMapper grpcConvertMapper;

    private final ConcurrentHashMap<String, BiFunction<QstructsDto, JSONObject, JSONObject>> QUESTION_PARSERS = new ConcurrentHashMap<String, BiFunction<QstructsDto, JSONObject, JSONObject>>() {
        {
            put("single", this::columnParseSingle);
            put("multiple", this::columnParseSingle);
            put("matrix_single", this::columnParseMatrixSingle);
            put("matrix_multiple", this::columnParseMatrixSingle);
            put("cascader", this::columnParseCascader);
            put("blank", this::columnParseBlank);
            put("multiple_blank", this::columnParseMultipleBlank);
            put("auto_table", this::columnParseAutoTable);
            put("evaluation", this::columnParseEvaluation);
            put("score", this::columnParseBlank);
            put("sort", this::columnParseSort);
            put("proportion", this::columnParseProportion);
            put("matrix_blank", this::columnParseMatrixBlank);
            put("matrix_score", this::columnParseMatrixBlank);
            put("timedelta", this::columnParseBlank);
            put("timestamp", this::columnParseBlank);
            put("geo", this::columnParseGeo);
            put("city", this::columnParseCity);
            put("descriptor", this::columnParseDescriptor);
        }

        private JSONObject columnParseSingle(QstructsDto dto, JSONObject question) {
            JSONObject parse_dict = new JSONObject();
            parse_dict.put("columns_set", new HashSet<>());
            parse_dict.put("sort_no", question.get("sort_no"));
            JSONObject col = new JSONObject();
            col.put("option", "");
            JSONObject query_params = new JSONObject();
            query_params.put("project_id", dto.getProjectId());
            query_params.put("gid", question.get("gid"));
            query_params.put("qtype", COLUMN_QTYPE_QUESTION.getNumber());
            query_params.put("status", COLUMN_STATUS_DELETE.getNumber());
            query_params.put("qstruct_id", dto.getId());
            columnParseCommonQuestion(question, parse_dict, col, query_params, null, null);
            //# 填空
            for (Object obj : question.getJSONArray("items")) {
                JSONObject item = (JSONObject) obj;
                if (!item.getBooleanValue("is_open")) {
                    continue;
                }
                col.put("otitle", item.getString("title"));
                col.put("option", "填空");
                query_params.put("project_id", dto.getProjectId());
                query_params.put("gid", question.get("gid"));
                query_params.put("ogid", item.get("gid"));
                query_params.put("qtype", COLUMN_QTYPE_OPEN.getNumber());
                query_params.put("status", COLUMN_STATUS_HIDE.getNumber());
                columnParseCommonOpen(question, parse_dict, col, query_params, item.getInteger("gid"), null);
                //nps生成nps客户类型列
                if ("nps".equals(question.getString("custom_qtype"))) {
                    col.put("option", "NPS客户类型");
                    query_params.put("project_id", dto.getProjectId());
                    query_params.put("gid", question.get("gid"));
                    query_params.put("qtype", COLUMN_QTYPE_NPS_CUSTOMER.getNumber());
                    query_params.put("status", COLUMN_STATUS_DELETE.getNumber());
                    columnParseCommonOther(question, parse_dict, col, query_params, null, null, COLUMN_STATUS_HIDE.getNumber());
                }
            }
            //# 评分
            columnParseCommonScores(question, parse_dict, dto);
            Integer sort_no = parse_dict.getInteger("sort_no");
            sort_no += 1000;
            parse_dict.put("sort_no", sort_no);
            return parse_dict;
        }

        private JSONObject columnParseMatrixSingle(QstructsDto dto, JSONObject question) {
            JSONObject parse_dict = new JSONObject();
            parse_dict.put("columns_set", new HashSet<>());
            parse_dict.put("sort_no", question.get("sort_no"));
            JSONObject col = new JSONObject();
            JSONObject query_params = new JSONObject();
            //# 题目
            for (Object obj : question.getJSONArray("rows_items")) {
                JSONObject row_item = (JSONObject) obj;
                col.put("rtitle", row_item.getString("title"));
                col.put("option", "");
                query_params.put("project_id", dto.getProjectId());
                query_params.put("gid", question.get("gid"));
                query_params.put("rgid", row_item.getInteger("gid"));
                query_params.put("qtype", COLUMN_QTYPE_QUESTION.getNumber());
                query_params.put("status", COLUMN_STATUS_DELETE.getNumber());
                query_params.put("qstruct_id", dto.getId());
                columnParseCommonQuestion(question, parse_dict, col, query_params, null, row_item.getInteger("gid"));
            }

            //# 填空
            for (Object obj : question.getJSONArray("rows_items")) {
                JSONObject row_item = (JSONObject) obj;
                if (!row_item.getBooleanValue("is_open")) {
                    continue;
                }
                col.put("otitle", row_item.getString("title"));
                col.put("option", "填空");
                query_params.put("project_id", dto.getProjectId());
                query_params.put("gid", question.get("gid"));
                query_params.put("rgid", row_item.getInteger("gid"));
                query_params.put("qtype", COLUMN_QTYPE_OPEN.getNumber());
                query_params.put("status", COLUMN_STATUS_DELETE.getNumber());
                query_params.put("qstruct_id", dto.getId());
                columnParseCommonOpen(question, parse_dict, col, query_params, 0, row_item.getInteger("gid"));
            }

            for (Object obj : question.getJSONArray("rows_items")) {
                JSONObject row_item = (JSONObject) obj;
                for (Object object : question.getJSONArray("items")) {
                    JSONObject item = (JSONObject) object;
                    if (!item.getBooleanValue("is_open")) {
                        continue;
                    }
                    col.put("rtitle", row_item.getString("title"));
                    col.put("otitle", item.getString("title"));
                    col.put("option", "填空");
                    query_params.put("project_id", dto.getProjectId());
                    query_params.put("gid", question.get("gid"));
                    query_params.put("rgid", row_item.getInteger("gid"));
                    query_params.put("ogid", item.getInteger("gid"));
                    query_params.put("qtype", COLUMN_QTYPE_OPEN.getNumber());
                    query_params.put("status", COLUMN_STATUS_DELETE.getNumber());
                    query_params.put("qstruct_id", dto.getId());
                    columnParseCommonOpen(question, parse_dict, col, query_params, item.getInteger("gid"), row_item.getInteger("gid"));
                }
            }
            //# 评分
            columnParseCommonScores(question, parse_dict, dto);
            Integer sort_no = parse_dict.getInteger("sort_no");
            sort_no += 1000;
            parse_dict.put("sort_no", sort_no);
            return parse_dict;
        }

        private JSONObject columnParseCascader(QstructsDto dto, JSONObject question) {
            JSONObject parse_dict = new JSONObject();
            parse_dict.put("columns_set", new HashSet<>());
            parse_dict.put("sort_no", question.get("sort_no"));
            //# 题目
            JSONArray cascader_qtypes = new JSONArray();
            cascader_qtypes.add(Lists.newArrayList(COLUMN_QTYPE_C1.getNumber(), "一级选项"));
            cascader_qtypes.add(Lists.newArrayList(COLUMN_QTYPE_C2.getNumber(), "二级选项"));
            cascader_qtypes.add(Lists.newArrayList(COLUMN_QTYPE_C3.getNumber(), "三级选项"));
            cascader_qtypes.add(Lists.newArrayList(COLUMN_QTYPE_C4.getNumber(), "四级选项"));
            for (int i = 0; i < cascader_qtypes.size(); i++) {
                List<Object> cascadertuple = cascader_qtypes.getJSONArray(i);

                JSONObject col = new JSONObject();
                col.put("option", cascadertuple.get(1));
                JSONObject query_params = new JSONObject();
                query_params.put("project_id", dto.getProjectId());
                query_params.put("gid", question.get("gid"));
                query_params.put("qtype", cascadertuple.get(0));
                query_params.put("status", COLUMN_STATUS_DELETE.getNumber());
                query_params.put("qstruct_id", dto.getId());
                columnParseCommonOther(question, parse_dict, col, query_params, null, null, COLUMN_STATUS_SHOW.getNumber());
            }
            //# 评分
            columnParseCommonScores(question, parse_dict, dto);
            Integer sort_no = parse_dict.getInteger("sort_no");
            sort_no += 1000;
            parse_dict.put("sort_no", sort_no);

            return parse_dict;
        }

        private JSONObject columnParseBlank(QstructsDto dto, JSONObject question) {
            JSONObject parse_dict = new JSONObject();
            parse_dict.put("columns_set", new HashSet<>());
            parse_dict.put("sort_no", question.get("sort_no"));
            JSONObject col = new JSONObject();
            col.put("option", "");
            JSONObject query_params = new JSONObject();
            query_params.put("project_id", dto.getProjectId());
            query_params.put("gid", question.get("gid"));
            query_params.put("qtype", COLUMN_QTYPE_QUESTION.getNumber());
            query_params.put("status", COLUMN_STATUS_DELETE.getNumber());
            query_params.put("qstruct_id", dto.getId());
            columnParseCommonQuestion(question, parse_dict, col, query_params, null, null);
            //nps生成nps客户类型列
            if ("nps".equals(question.getString("custom_qtype"))) {
                col.put("option", "NPS客户类型");
                query_params.put("project_id", dto.getProjectId());
                query_params.put("gid", question.get("gid"));
                query_params.put("qtype", COLUMN_QTYPE_NPS_CUSTOMER.getNumber());
                query_params.put("status", COLUMN_STATUS_DELETE.getNumber());
                columnParseCommonOther(question, parse_dict, col, query_params, null, null, COLUMN_STATUS_HIDE.getNumber());
            }
            //# 评分
            columnParseCommonScores(question, parse_dict, dto);
            Integer sort_no = parse_dict.getInteger("sort_no");
            sort_no += 1000;
            parse_dict.put("sort_no", sort_no);
            return parse_dict;
        }

        private JSONObject columnParseGeo(QstructsDto dto, JSONObject question) {
            JSONObject parse_dict = new JSONObject();
            parse_dict.put("columns_set", new HashSet<>());
            parse_dict.put("sort_no", question.get("sort_no"));
            JSONObject col = new JSONObject();
            col.put("rtitle", "");
            col.put("otitle", "");
            col.put("option", "");
            JSONObject query_params = new JSONObject();
            query_params.put("project_id", dto.getProjectId());
            query_params.put("gid", question.get("gid"));
            query_params.put("qtype", COLUMN_QTYPE_QUESTION.getNumber());
            query_params.put("status", COLUMN_STATUS_DELETE.getNumber());
            query_params.put("qstruct_id", dto.getId());
            columnParseCommonQuestion(question, parse_dict, col, query_params, null, null);
            //# 填空
            JSONArray geo_qtypes = new JSONArray();
            geo_qtypes.add(Lists.newArrayList(COLUMN_QTYPE_PROVINCE.getNumber(), "省份"));
            geo_qtypes.add(Lists.newArrayList(COLUMN_QTYPE_CITY.getNumber(), "城市"));
            geo_qtypes.add(Lists.newArrayList(COLUMN_QTYPE_DISTRICT.getNumber(), "区县"));
            geo_qtypes.add(Lists.newArrayList(COLUMN_QTYPE_ADDRESS.getNumber(), "详细地址"));
            geo_qtypes.add(Lists.newArrayList(COLUMN_QTYPE_COORDINATE.getNumber(), "经纬度"));
            for (int i = 0; i < geo_qtypes.size(); i++) {
                List<Object> geotuple = geo_qtypes.getJSONArray(i);
                col.put("rtitle", "");
                col.put("otitle", "");
                col.put("option", geotuple.get(1));
                query_params.put("project_id", dto.getProjectId());
                query_params.put("gid", question.get("gid"));
                query_params.put("qtype", geotuple.get(0));
                query_params.put("status", COLUMN_STATUS_DELETE.getNumber());
                query_params.put("qstruct_id", dto.getId());
                columnParseCommonOther(question, parse_dict, col, query_params, null, null, COLUMN_STATUS_HIDE.getNumber());
            }
            //# 评分
            columnParseCommonScores(question, parse_dict, dto);
            Integer sort_no = parse_dict.getInteger("sort_no");
            sort_no += 1000;
            parse_dict.put("sort_no", sort_no);

            return parse_dict;
        }

        private JSONObject columnParseCity(QstructsDto dto, JSONObject question) {
            JSONObject parse_dict = new JSONObject();
            parse_dict.put("columns_set", new HashSet<>());
            parse_dict.put("sort_no", question.get("sort_no"));
            //# 填空
            JSONArray geo_qtypes = new JSONArray();
            geo_qtypes.add(Lists.newArrayList(COLUMN_QTYPE_PROVINCE.getNumber(), "省份"));
            geo_qtypes.add(Lists.newArrayList(COLUMN_QTYPE_CITY.getNumber(), "城市"));
            geo_qtypes.add(Lists.newArrayList(COLUMN_QTYPE_DISTRICT.getNumber(), "区县"));
            geo_qtypes.add(Lists.newArrayList(COLUMN_QTYPE_ADDRESS.getNumber(), "详细地址"));
            for (int i = 0; i < geo_qtypes.size(); i++) {
                List<Object> geotuple = geo_qtypes.getJSONArray(i);
                JSONObject col = new JSONObject();
                col.put("rtitle", "");
                col.put("otitle", "");
                col.put("option", geotuple.get(1));
                JSONObject query_params = new JSONObject();
                query_params.put("project_id", dto.getProjectId());
                query_params.put("gid", question.get("gid"));
                query_params.put("qtype", geotuple.get(0));
                query_params.put("status", COLUMN_STATUS_DELETE.getNumber());
                query_params.put("qstruct_id", dto.getId());
                columnParseCommonOther(question, parse_dict, col, query_params, null, null, COLUMN_STATUS_SHOW.getNumber());
            }
            //# 评分
            columnParseCommonScores(question, parse_dict, dto);
            Integer sort_no = parse_dict.getInteger("sort_no");
            sort_no += 1000;
            parse_dict.put("sort_no", sort_no);

            return parse_dict;
        }

        private JSONObject columnParseMultipleBlank(QstructsDto dto, JSONObject question) {
            JSONObject parse_dict = new JSONObject();
            parse_dict.put("columns_set", new HashSet<>());
            parse_dict.put("sort_no", question.get("sort_no"));
            for (Object obj : question.getJSONArray("items")) {
                JSONObject option = (JSONObject) obj;
                JSONObject col = new JSONObject();
                col.put("otitle", option.getString("title"));
                col.put("option", "");
                JSONObject query_params = new JSONObject();
                query_params.put("project_id", dto.getProjectId());
                query_params.put("gid", question.get("gid"));
                //query_params.put("ogid", option.get("gid"));
                query_params.put("qtype", COLUMN_QTYPE_QUESTION.getNumber());
                query_params.put("status", COLUMN_STATUS_DELETE.getNumber());
                query_params.put("qstruct_id", dto.getId());
                columnParseCommonQuestion(question, parse_dict, col, query_params, option.getInteger("gid"), null);
            }
            //# 评分
            columnParseCommonScores(question, parse_dict, dto);
            Integer sort_no = parse_dict.getInteger("sort_no");
            sort_no += 1000;
            parse_dict.put("sort_no", sort_no);
            return parse_dict;
        }

        private JSONObject columnParseAutoTable(QstructsDto dto, JSONObject question) {
            JSONObject parse_dict = new JSONObject();
            parse_dict.put("columns_set", new HashSet<>());
            parse_dict.put("sort_no", question.get("sort_no"));
            JSONObject col = new JSONObject();
            col.put("option", "");
            JSONObject query_params = new JSONObject();
            query_params.put("project_id", dto.getProjectId());
            query_params.put("gid", question.get("gid"));
            query_params.put("qtype", COLUMN_QTYPE_QUESTION.getNumber());
            query_params.put("status", COLUMN_STATUS_DELETE.getNumber());
            query_params.put("qstruct_id", dto.getId());
            columnParseCommonQuestion(question, parse_dict, col, query_params, null, null);
            //# 评分
            columnParseCommonScores(question, parse_dict, dto);
            Integer sort_no = parse_dict.getInteger("sort_no");
            sort_no += 1000;
            parse_dict.put("sort_no", sort_no);
            return parse_dict;
        }

        private JSONObject columnParseEvaluation(QstructsDto dto, JSONObject question) {
            JSONObject parse_dict = new JSONObject();
            parse_dict.put("columns_set", new HashSet<>());
            parse_dict.put("sort_no", question.get("sort_no"));
            JSONObject col = new JSONObject();
            // 题目
            col.put("option", "");
            JSONObject query_params = new JSONObject();
            query_params.put("project_id", dto.getProjectId());
            query_params.put("gid", question.get("gid"));
            query_params.put("qtype", COLUMN_QTYPE_QUESTION.getNumber());
            query_params.put("status", COLUMN_STATUS_DELETE.getNumber());
            query_params.put("qstruct_id", dto.getId());
            columnParseCommonQuestion(question, parse_dict, col, query_params, null, null);
            //# 标签
            col.put("option", "标签");
            query_params.put("project_id", dto.getProjectId());
            query_params.put("gid", question.get("gid"));
            query_params.put("qtype", COLUMN_QTYPE_TAG.getNumber());
            query_params.put("status", COLUMN_STATUS_DELETE.getNumber());
            query_params.put("qstruct_id", dto.getId());
            columnParseCommonOther(question, parse_dict, col, query_params, null, null, COLUMN_STATUS_SHOW.getNumber());
            //# 留言
            col.put("option", "留言");
            query_params.put("project_id", dto.getProjectId());
            query_params.put("gid", question.get("gid"));
            query_params.put("qtype", COLUMN_QTYPE_OPEN.getNumber());
            query_params.put("status", COLUMN_STATUS_DELETE.getNumber());
            query_params.put("qstruct_id", dto.getId());
            query_params.put("flag", "留言");
            columnParseCommonOpen(question, parse_dict, col, query_params, null, null);
            //# 评分
            columnParseCommonScores(question, parse_dict, dto);
            Integer sort_no = parse_dict.getInteger("sort_no");
            sort_no += 1000;
            parse_dict.put("sort_no", sort_no);
            return parse_dict;
        }

        private JSONObject columnParseSort(QstructsDto dto, JSONObject question) {
            JSONObject parse_dict = new JSONObject();
            parse_dict.put("columns_set", new HashSet<>());
            parse_dict.put("sort_no", question.get("sort_no"));
            JSONObject query_params = new JSONObject();
            JSONObject col = new JSONObject();
            for (Object obj : question.getJSONArray("items")) {
                JSONObject option = (JSONObject) obj;
                col.put("otitle", option.getString("title"));
                col.put("option", "");
                query_params.put("project_id", dto.getProjectId());
                query_params.put("gid", question.get("gid"));
                //query_params.put("ogid", option.get("gid"));
                query_params.put("qtype", COLUMN_QTYPE_QUESTION.getNumber());
                query_params.put("status", COLUMN_STATUS_DELETE.getNumber());
                query_params.put("qstruct_id", dto.getId());
                columnParseCommonQuestion(question, parse_dict, col, query_params, option.getInteger("gid"), null);
            }
            //# 填空
            for (Object obj : question.getJSONArray("items")) {
                JSONObject item = (JSONObject) obj;
                if (!item.getBooleanValue("is_open")) {
                    continue;
                }
                col.put("otitle", item.getString("title"));
                col.put("option", "填空");
                query_params.put("project_id", dto.getProjectId());
                query_params.put("gid", question.get("gid"));
                query_params.put("ogid", item.getInteger("gid"));
                query_params.put("qtype", COLUMN_QTYPE_OPEN.getNumber());
                query_params.put("status", COLUMN_STATUS_DELETE.getNumber());
                query_params.put("qstruct_id", dto.getId());
                columnParseCommonOpen(question, parse_dict, col, query_params, null, item.getInteger("gid"));
            }
            //# 评分
            columnParseCommonScores(question, parse_dict, dto);
            Integer sort_no = parse_dict.getInteger("sort_no");
            sort_no += 1000;
            parse_dict.put("sort_no", sort_no);
            return parse_dict;
        }

        private JSONObject columnParseProportion(QstructsDto dto, JSONObject question) {
            JSONObject parse_dict = new JSONObject();
            parse_dict.put("columns_set", new HashSet<>());
            parse_dict.put("sort_no", question.get("sort_no"));
            JSONObject query_params = new JSONObject();
            JSONObject col = new JSONObject();
            for (Object obj : question.getJSONArray("items")) {
                JSONObject items = (JSONObject) obj;
                col.put("otitle", items.getString("title"));
                col.put("option", "");
                query_params.put("project_id", dto.getProjectId());
                query_params.put("gid", question.get("gid"));
                query_params.put("ogid", items.get("gid"));
                query_params.put("qtype", COLUMN_QTYPE_QUESTION.getNumber());
                query_params.put("status", COLUMN_STATUS_DELETE.getNumber());
                query_params.put("qstruct_id", dto.getId());
                columnParseCommonQuestion(question, parse_dict, col, query_params, items.getInteger("gid"), null);
            }
            //# 评分
            columnParseCommonScores(question, parse_dict, dto);
            Integer sort_no = parse_dict.getInteger("sort_no");
            sort_no += 1000;
            parse_dict.put("sort_no", sort_no);
            return parse_dict;
        }

        private JSONObject columnParseMatrixBlank(QstructsDto dto, JSONObject question) {
            JSONObject parse_dict = new JSONObject();
            parse_dict.put("columns_set", new HashSet<>());
            parse_dict.put("sort_no", question.get("sort_no"));
            JSONObject col = new JSONObject();
            JSONObject query_params = new JSONObject();
            //# 题目
            for (Object obj : question.getJSONArray("rows_items")) {
                JSONObject row_item = (JSONObject) obj;
                for (Object object : question.getJSONArray("items")) {
                    JSONObject item = (JSONObject) object;
                    col.put("rtitle", row_item.getString("title") + "-" + item.getString("title"));
                    col.put("otitle", "");
                    col.put("option", "");
                    query_params.put("project_id", dto.getProjectId());
                    query_params.put("gid", question.get("gid"));
                    /*query_params.put("rgid", row_item.getInteger("gid"));
                    query_params.put("ogid", item.getInteger("gid"));*/
                    query_params.put("qtype", COLUMN_QTYPE_QUESTION.getNumber());
                    query_params.put("status", COLUMN_STATUS_DELETE.getNumber());
                    query_params.put("qstruct_id", dto.getId());
                    columnParseCommonQuestion(question, parse_dict, col, query_params, item.getInteger("gid"), row_item.getInteger("gid"));
                }
            }
            //# 填空
            for (Object obj : question.getJSONArray("rows_items")) {
                JSONObject row_item = (JSONObject) obj;
                if (!row_item.getBooleanValue("is_open")) {
                    continue;
                }
                col.put("rtitle", row_item.getString("title"));
                col.put("otitle", "");
                col.put("option", "填空");
                query_params.put("project_id", dto.getProjectId());
                query_params.put("gid", question.get("gid"));
                query_params.put("rgid", row_item.getInteger("gid"));
                query_params.put("qtype", COLUMN_QTYPE_OPEN.getNumber());
                query_params.put("status", COLUMN_STATUS_DELETE.getNumber());
                query_params.put("qstruct_id", dto.getId());
                columnParseCommonOpen(question, parse_dict, col, query_params, 0, row_item.getInteger("gid"));
            }
            //# 评分
            columnParseCommonScores(question, parse_dict, dto);
            Integer sort_no = parse_dict.getInteger("sort_no");
            sort_no += 1000;
            parse_dict.put("sort_no", sort_no);
            return parse_dict;
        }

        private JSONObject columnParseDescriptor(QstructsDto dto, JSONObject question) {
            JSONObject parse_dict = new JSONObject();
            parse_dict.put("columns_set", new HashSet<>());
            parse_dict.put("sort_no", question.get("sort_no"));
            JSONObject col = new JSONObject();
            col.put("rtitle", "");
            col.put("otitle", "");
            col.put("option", "");
            JSONObject query_params = new JSONObject();
            query_params.put("project_id", dto.getProjectId());
            query_params.put("gid", question.get("gid"));
            query_params.put("qtype", COLUMN_QTYPE_QUESTION.getNumber());
            query_params.put("status", COLUMN_STATUS_DELETE.getNumber());
            query_params.put("qstruct_id", dto.getId());
            columnParseCommonQuestion(question, parse_dict, col, query_params, null, null);
            return parse_dict;
        }

        private void columnParseCommonQuestion(JSONObject question, JSONObject parse_dict, JSONObject col, JSONObject query_params, Integer ogid, Integer rgid) {
            JSONObject custom_attr = new JSONObject();
            custom_attr.put("cid", question.get("cid"));
            custom_attr.put("title", question.get("title"));
            custom_attr.put("rtitle", ObjectUtil.isEmpty(col.get("rtitle")) ? "" : col.get("rtitle"));
            custom_attr.put("otitle", ObjectUtil.isEmpty(col.get("otitle")) ? "" : col.get("otitle"));
            custom_attr.put("option", ObjectUtil.isEmpty(col.get("option")) ? "" : col.get("option"));
            SurveyColumn surveyColumn = new SurveyColumn();
            surveyColumn.setProjectId(query_params.getString("project_id"));
            surveyColumn.setGid(question.getInteger("gid"));
            surveyColumn.setDispType(question.getString("qtype"));
            surveyColumn.setQtype(COLUMN_QTYPE_QUESTION.getNumber());
            surveyColumn.setCtype(COLUMN_CTYPE_QUESTION.getNumber());
            surveyColumn.setSortNo(parse_dict.getInteger("sort_no"));
            surveyColumn.setCustomAttr(custom_attr.toJSONString());
            if (Objects.nonNull(ogid)) {
                surveyColumn.setOgid(ogid);
            }
            if (Objects.nonNull(rgid)) {
                surveyColumn.setRgid(rgid);
            }
            String obj_id;
            SurveyColumn column = columnMapper.selectOne(new LambdaQueryWrapper<SurveyColumn>()
                    .eq(SurveyColumn::getProjectId, query_params.getString("project_id"))
                    .eq(SurveyColumn::getGid, query_params.getInteger("gid"))
                    .eq(SurveyColumn::getQtype, query_params.getInteger("qtype"))
                    .ne(SurveyColumn::getStatus, query_params.getInteger("status"))
                    .eq(Objects.nonNull(rgid), SurveyColumn::getRgid, rgid)
                    .eq(Objects.nonNull(ogid), SurveyColumn::getOgid, ogid));
            if (ObjectUtil.isNotEmpty(column)) {
                JSONArray qstruct_ids = JSONArray.parseArray(column.getQstructIds());
                obj_id = column.getId();
                if (!qstruct_ids.contains(query_params.get("qstruct_id"))) {
                    qstruct_ids.add(query_params.get("qstruct_id"));
                    columnMapper.update(null, new LambdaUpdateWrapper<SurveyColumn>()
                            .set(SurveyColumn::getQstructIds, qstruct_ids.toJSONString())
                            .set(SurveyColumn::getSortNo, parse_dict.getInteger("sort_no"))
                            .set(SurveyColumn::getCustomAttr, custom_attr.toJSONString())
                            .eq(SurveyColumn::getId, column.getId()));
                }
            } else {
                JSONArray qstruct_ids = new JSONArray();
                qstruct_ids.add(query_params.getString("qstruct_id"));
                surveyColumn.setStatus(COLUMN_STATUS_SHOW.getNumber());
                surveyColumn.setQstructIds(qstruct_ids.toJSONString());
                columnMapper.insert(surveyColumn);
                obj_id = surveyColumn.getId();
            }

            query_params.clear();

            Set<String> columns_set = Optional.of(new HashSet<>(parse_dict.getJSONArray("columns_set").toJavaList(String.class))).orElse(new HashSet<>());
            columns_set.add(obj_id);
            parse_dict.put("columns_set", columns_set);
            Integer sort_no = parse_dict.getInteger("sort_no");
            sort_no += 1;
            parse_dict.put("sort_no", sort_no);
        }

        private void columnParseCommonOpen(JSONObject question, JSONObject parse_dict, JSONObject col, JSONObject query_params, Integer ogid, Integer rgid) {
            JSONObject custom_attr = new JSONObject();
            custom_attr.put("cid", question.get("cid"));
            custom_attr.put("title", question.get("title"));
            custom_attr.put("rtitle", ObjectUtil.isEmpty(col.get("rtitle")) ? "" : col.get("rtitle"));
            custom_attr.put("otitle", ObjectUtil.isEmpty(col.get("otitle")) ? "" : col.get("otitle"));
            custom_attr.put("option", ObjectUtil.isEmpty(col.get("option")) ? "" : col.get("option"));
            SurveyColumn surveyColumn = new SurveyColumn();
            surveyColumn.setProjectId(query_params.getString("project_id"));
            surveyColumn.setGid(question.getInteger("gid"));
            surveyColumn.setDispType(question.getString("qtype"));
            surveyColumn.setQtype(COLUMN_QTYPE_OPEN.getNumber());
            surveyColumn.setCtype(COLUMN_CTYPE_QUESTION.getNumber());
            surveyColumn.setSortNo(parse_dict.getInteger("sort_no"));
            surveyColumn.setCustomAttr(custom_attr.toJSONString());
            if (ObjectUtil.isNotEmpty(ogid)) {
                surveyColumn.setOgid(ogid);
            }
            if (ObjectUtil.isNotEmpty(rgid)) {
                surveyColumn.setRgid(rgid);
            }
            String obj_id;
            SurveyColumn column = columnMapper.selectOne(new LambdaQueryWrapper<SurveyColumn>()
                    .eq(SurveyColumn::getProjectId, query_params.getString("project_id"))
                    .eq(SurveyColumn::getGid, query_params.getInteger("gid"))
                    .eq(SurveyColumn::getQtype, query_params.getInteger("qtype"))
                    .ne(SurveyColumn::getStatus, query_params.getInteger("status")));
            if (ObjectUtil.isNotEmpty(column)) {
                JSONArray qstruct_ids = JSONArray.parseArray(column.getQstructIds());
                obj_id = column.getId();
                if (!qstruct_ids.contains(query_params.get("qstruct_id"))) {
                    qstruct_ids.add(query_params.get("qstruct_id"));
                    columnMapper.update(null, new LambdaUpdateWrapper<SurveyColumn>()
                            .set(SurveyColumn::getQstructIds, qstruct_ids.toJSONString())
                            .set(SurveyColumn::getSortNo, parse_dict.getInteger("sort_no"))
                            .set(SurveyColumn::getCustomAttr, custom_attr.toJSONString())
                            .eq(SurveyColumn::getId, column.getId()));
                }
            } else {
                JSONArray qstruct_ids = new JSONArray();
                qstruct_ids.add(query_params.getString("qstruct_id"));
                if (ObjectUtil.isNotEmpty(query_params.get("flag"))) {
                    surveyColumn.setStatus(COLUMN_STATUS_SHOW.getNumber());
                } else {
                    surveyColumn.setStatus(COLUMN_STATUS_HIDE.getNumber());
                }
                surveyColumn.setQstructIds(qstruct_ids.toJSONString());
                columnMapper.insert(surveyColumn);
                obj_id = surveyColumn.getId();
            }

            query_params.clear();

            Set<String> columns_set = Optional.of(new HashSet<>(parse_dict.getJSONArray("columns_set").toJavaList(String.class))).orElse(new HashSet<>());
            columns_set.add(obj_id);
            parse_dict.put("columns_set", columns_set);
            Integer sort_no = parse_dict.getInteger("sort_no");
            sort_no += 1;
            parse_dict.put("sort_no", sort_no);
        }

        private void columnParseCommonOther(JSONObject question, JSONObject parse_dict, JSONObject col, JSONObject query_params, Integer ogid, Integer rgid, int status) {
            JSONObject custom_attr = new JSONObject();
            custom_attr.put("cid", question.get("cid"));
            custom_attr.put("title", question.get("title"));
            custom_attr.put("rtitle", ObjectUtil.isEmpty(col.get("rtitle")) ? "" : col.get("rtitle"));
            custom_attr.put("otitle", ObjectUtil.isEmpty(col.get("otitle")) ? "" : col.get("otitle"));
            custom_attr.put("option", ObjectUtil.isEmpty(col.get("option")) ? "" : col.get("option"));
            SurveyColumn surveyColumn = new SurveyColumn();
            surveyColumn.setProjectId(query_params.getString("project_id"));
            surveyColumn.setGid(question.getInteger("gid"));
            surveyColumn.setDispType(question.getString("qtype"));
            surveyColumn.setQtype(query_params.getInteger("qtype"));
            surveyColumn.setCtype(COLUMN_CTYPE_QUESTION.getNumber());
            surveyColumn.setSortNo(parse_dict.getInteger("sort_no"));
            surveyColumn.setCustomAttr(custom_attr.toJSONString());
            if (ObjectUtil.isNotEmpty(ogid)) {
                surveyColumn.setOgid(ogid);
            }
            if (ObjectUtil.isNotEmpty(rgid)) {
                surveyColumn.setRgid(rgid);
            }
            String obj_id;
            SurveyColumn column = columnMapper.selectOne(new LambdaQueryWrapper<SurveyColumn>()
                    .eq(SurveyColumn::getProjectId, query_params.getString("project_id"))
                    .eq(SurveyColumn::getGid, query_params.getInteger("gid"))
                    .eq(SurveyColumn::getQtype, query_params.getInteger("qtype"))
                    .ne(SurveyColumn::getStatus, query_params.getInteger("status")));
            if (ObjectUtil.isNotEmpty(column)) {
                JSONArray qstruct_ids = JSONArray.parseArray(column.getQstructIds());
                obj_id = column.getId();
                if (!qstruct_ids.contains(query_params.get("qstruct_id"))) {
                    qstruct_ids.add(query_params.get("qstruct_id"));
                    columnMapper.update(null, new LambdaUpdateWrapper<SurveyColumn>()
                            .set(SurveyColumn::getQstructIds, qstruct_ids.toJSONString())
                            .set(SurveyColumn::getSortNo, parse_dict.getString("sort_no"))
                            .set(SurveyColumn::getCustomAttr, custom_attr.toJSONString())
                            .eq(SurveyColumn::getId, column.getId()));
                }
            } else {
                JSONArray qstruct_ids = new JSONArray();
                qstruct_ids.add(query_params.getString("qstruct_id"));
                surveyColumn.setStatus(status);
                surveyColumn.setQstructIds(qstruct_ids.toJSONString());
                columnMapper.insert(surveyColumn);
                obj_id = surveyColumn.getId();
            }

            query_params.clear();

            Set<String> columns_set = Optional.of(new HashSet<>(parse_dict.getJSONArray("columns_set").toJavaList(String.class))).orElse(new HashSet<>());
            columns_set.add(obj_id);
            parse_dict.put("columns_set", columns_set);
            Integer sort_no = parse_dict.getInteger("sort_no");
            sort_no += 1;
            parse_dict.put("sort_no", sort_no);
        }

        private void columnParseCommonScores(JSONObject question, JSONObject parse_dict, QstructsDto dto) {
            if (ObjectUtil.isNotEmpty(dto.getScores()) && (dto.getScores()).contains(question.getString("gid"))) {
                JSONObject custom_attr = new JSONObject();
                custom_attr.put("cid", question.get("cid"));
                custom_attr.put("title", question.get("title"));
                custom_attr.put("rtitle", "");
                custom_attr.put("otitle", "");
                custom_attr.put("option", "评分");
                SurveyColumn surveyColumn = new SurveyColumn();
                surveyColumn.setProjectId(dto.getProjectId());
                surveyColumn.setGid(question.getInteger("gid"));
                surveyColumn.setDispType(question.getString("qtype"));
                surveyColumn.setQtype(COLUMN_QTYPE_SCORE.getNumber());
                surveyColumn.setCtype(COLUMN_CTYPE_QUESTION.getNumber());
                surveyColumn.setSortNo(parse_dict.getInteger("sort_no"));
                surveyColumn.setCustomAttr(custom_attr.toJSONString());
                String obj_id;
                SurveyColumn column = columnMapper.selectOne(new LambdaQueryWrapper<SurveyColumn>()
                        .eq(SurveyColumn::getProjectId, dto.getProjectId())
                        .eq(SurveyColumn::getGid, question.getInteger("gid"))
                        .eq(SurveyColumn::getQtype, COLUMN_QTYPE_SCORE.getNumber())
                        .ne(SurveyColumn::getStatus, COLUMN_STATUS_DELETE.getNumber()));
                if (ObjectUtil.isNotEmpty(column)) {
                    JSONArray qstruct_ids = JSONArray.parseArray(column.getQstructIds());
                    obj_id = column.getId();
                    if (!qstruct_ids.contains(dto.getId())) {
                        qstruct_ids.add(dto.getId());
                        columnMapper.update(null, new LambdaUpdateWrapper<SurveyColumn>()
                                .set(SurveyColumn::getQstructIds, qstruct_ids.toJSONString())
                                .set(SurveyColumn::getSortNo, parse_dict.getString("sort_no"))
                                .set(SurveyColumn::getCustomAttr, custom_attr.toJSONString())
                                .eq(SurveyColumn::getId, column.getId()));
                    }
                } else {
                    JSONArray qstruct_ids = new JSONArray();
                    qstruct_ids.add(dto.getId());
                    surveyColumn.setStatus(COLUMN_STATUS_HIDE.getNumber());
                    surveyColumn.setQstructIds(qstruct_ids.toJSONString());
                    columnMapper.insert(surveyColumn);
                    obj_id = surveyColumn.getId();
                }

                Set<String> columns_set = Optional.of(new HashSet<>(parse_dict.getJSONArray("columns_set").toJavaList(String.class))).orElse(new HashSet<>());
                columns_set.add(obj_id);
                parse_dict.put("columns_set", columns_set);
                Integer sort_no = parse_dict.getInteger("sort_no");
                sort_no += 1;
                parse_dict.put("sort_no", sort_no);
            }
        }
    };

    @Override
    public void clearRspdPropCache(JSONObject msg) {
        redisService.del(String.format(RSPD_PROP_FORMAT, msg.getString("project_id")));
    }

    @Override
    public void surveyCreateColumns(CreateColumnsDto dto) {
        String project_id = dto.getProjectId();
        String company_id = dto.getCompanyId();
        if (StringUtils.isEmpty(project_id)) {
            throw new BusinessException("创建答卷数据列需要project_id");
        }
        if (StringUtils.isEmpty(company_id)) {
            throw new BusinessException("创建答卷数据列需要company_id");
        }
        createColumnsRelsproject(dto.getProjectId(), null, dto.getCompanyId());
    }

    @Override
    public List<SurveyColumnVo> createColumnsRelsproject(String project_id, String root_group_id, String companyID) {
        //if (StringUtils.isEmpty(root_group_id)) {
        //    OrgInfoDto org = userGrpcClient.getOrganization(companyID);
        //    if (ObjectUtil.isEmpty(org)) {
        //        throw new BusinessException("get_organization failed, companyID=" + companyID);
        //    }
        //    root_group_id = org.getRootGroupId();
        //}
        createMetaColumnsFront(project_id, root_group_id, companyID);
        createQColumns(project_id);
        createMetaColumnsBehind(project_id);
        createScoreColumns(project_id);
        createMemberColumns(project_id);
        createExtParamsColumns(project_id);
        return surveyCommonService.getColumnsNeDelete(project_id, new JSONObject(), companyID);
    }

    private void createExtParamsColumns(String id) {
        //获取最新的外部参数
        SurveyExtParameter extParam = surveyExtParameterService.getExtParam(id, null, null);
        Map<String, JSONObject> ext_params_map = new HashMap<>();
        //获取外部参数
        List<JSONObject> extParamData;
        if (Objects.nonNull(extParam) && CollUtil.isNotEmpty(extParamData = JSONArray.parseArray(extParam.getData(), JSONObject.class))) {
            extParamData.forEach(v -> ext_params_map.put(v.getString("name"), v));
        }

        // 外部参数改版，列里面要加进来新的外部参数数据
        // 获取问卷下所有投放使用过的外部参数
        JSONObject query = new JSONObject();
        query.put("survey_id", id);
        List<DeliverExtParamsDto> deliver_ext_param_list = deliverGrpcClient.listDeliverExtparams(query);
        deliver_ext_param_list.forEach(v -> {
            JSONObject data = new JSONObject(4);
            data.put("name", v.getExtparamName());
            data.put("dtype", v.getExtparamType());
            ext_params_map.put(v.getExtparamName(), data);
        });

        //获取所有已有的外部参数列
        AtomicInteger sort_no = new AtomicInteger();
        LambdaQueryWrapper<SurveyColumn> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(SurveyColumn::getProjectId, id).eq(SurveyColumn::getCtype, COLUMN_CTYPE_EXTERNAL.getNumber());
        wrapper.orderByAsc(SurveyColumn::getCtype);
        wrapper.orderByAsc(SurveyColumn::getSortNo);
        List<SurveyColumn> columnList = columnMapper.selectList(wrapper);
        columnList.forEach(column -> {
            SurveyColumn surveyColumn = new SurveyColumn();
            surveyColumn.setId(column.getId());
            if (ext_params_map.containsKey(column.getCol())) {
                //之前存在的数据列，并且现在还存在，更新一下
                JSONObject ext_param = ext_params_map.remove(column.getCol());
                surveyColumn.setCol(ext_param.getString("name"));
                surveyColumn.setStatus(column.getStatus().equals(COLUMN_STATUS_DELETE.getNumber()) ? COLUMN_STATUS_HIDE.getNumber() : column.getStatus());
                surveyColumn.setSortNo(sort_no.get());
            } else {
                //之前存在的数据列，但是现在不存在，修改一下状态为删除
                surveyColumn.setStatus(COLUMN_STATUS_DELETE.getNumber());
            }
            columnMapper.updateById(surveyColumn);
            sort_no.addAndGet(1);
        });
        if (CollUtil.isNotEmpty(ext_params_map)) {
            List<SurveyColumn> data = new ArrayList<>(ext_params_map.size());
            //当前列中不存在的数据，要新建
            ext_params_map.keySet().forEach(k -> {
                SurveyColumn column = new SurveyColumn();
                column.setCtype(COLUMN_CTYPE_EXTERNAL.getNumber());
                column.setProjectId(id);
                column.setCol(k);
                column.setSortNo(sort_no.get());
                column.setStatus(COLUMN_STATUS_HIDE.getNumber());
                column.setQstructIds("[]");
                column.setQtype(COLUMN_QTYPE_QUESTION.getNumber());
                column.setCustomAttr("{}");
                data.add(column);
                sort_no.addAndGet(1);
            });
            columnMapper.insertBatchSomeColumn(data);
        }
    }

    private void memberColumnProcess(Map<String, SurveyColumn> col_columns_map, SurveyColumn member_column) {
        col_columns_map.put(member_column.getCol(), member_column);
        //错误的列删掉
        if (!COLUMNS_MEMBER.contains(member_column.getCol())) {
            member_column.setStatus(COLUMN_STATUS_DELETE.getNumber());
            columnMapper.updateById(member_column);
        }
    }

    private void createMemberColumns(String id) {
        LambdaQueryWrapper<SurveyColumn> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(SurveyColumn::getProjectId, id);
        queryWrapper.eq(SurveyColumn::getCtype, COLUMN_CTYPE_MEMBER.getNumber());
        queryWrapper.ne(SurveyColumn::getStatus, COLUMN_STATUS_DELETE.getNumber());
        List<SurveyColumn> surveyColumnsList = columnMapper.selectList(queryWrapper);
        Map<String, SurveyColumn> col_columns_map = new HashMap<>();
        surveyColumnsList.forEach(v -> memberColumnProcess(col_columns_map, v));
        int sort_no = 0;
        List<String> metas = new ArrayList<>(COLUMNS_MEMBER);
        for (String v : metas) {
            //存在修改sort_no , 不存在就新建
            SurveyColumn surveyColumn;
            if (col_columns_map.containsKey(v) && (surveyColumn = col_columns_map.get(v)).getStatus() != COLUMN_STATUS_DELETE.getNumber()) {
                surveyColumn.setSortNo(sort_no);
                sort_no += 1;
                columnMapper.updateById(surveyColumn);
            } else {
                sort_no = findOneAndUpdateColumns(id, COLUMN_CTYPE_MEMBER.getNumber(), v, sort_no, COLUMN_STATUS_HIDE.getNumber());
            }
        }
    }

    /*
    前半部分元数据
     */
    private void createMetaColumnsFront(String project_id, String root_group_id, String companyID) {
        List<SurveyColumn> front_columns = columnMapper.selectList(new LambdaQueryWrapper<SurveyColumn>()
                .eq(SurveyColumn::getProjectId, project_id)
                .eq(SurveyColumn::getCol, COLUMNS_META_FRONT.get(0))
                .eq(SurveyColumn::getCtype, ColumnCtype.COLUMN_CTYPE_META_FRONT.getNumber())
                .ne(SurveyColumn::getStatus, COLUMN_STATUS_DELETE.getNumber()));
        int sort_no = 0;
        Map<String, Object> col_columns_map = new HashMap<>();
        if (front_columns.size() > 0) {
            front_columns.forEach(surveyColumn -> {
                col_columns_map.put(surveyColumn.getCol(), surveyColumn);
                if (!COLUMNS_META_FRONT.contains(surveyColumn.getCol())) {
                    surveyColumn.setStatus(COLUMN_STATUS_DELETE.getNumber());
                    columnMapper.updateById(surveyColumn);
                }
            });
        }
        List<String> metas = new ArrayList<>(COLUMNS_META_FRONT);
        if (/*ObjectUtil.isNotEmpty(root_group_id) && */ObjectUtil.isNotEmpty(companyID)) {
            List<Map<String, String>> grouplevels = userGrpcClient.getGrouplevels(companyID);
            grouplevels.forEach(a -> metas.add(a.get("title")));
            for (String meta : metas) {
                SurveyColumn surveyColumn = (SurveyColumn) col_columns_map.get(meta);
                if (col_columns_map.containsKey(meta) && surveyColumn.getStatus() != COLUMN_STATUS_DELETE.getNumber()) {
                    surveyColumn.setSortNo(sort_no);
                    sort_no += 1;
                    columnMapper.updateById(surveyColumn);
                } else {
                    sort_no = findOneAndUpdateColumns(project_id, COLUMN_CTYPE_META_FRONT.getNumber(), meta, sort_no, null);
                }
            }
        }
    }

    private Integer findOneAndUpdateColumns(String projectId, Integer ctype, String meta, Integer sort_no, Integer status) {
        LambdaQueryWrapper<SurveyColumn> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(SurveyColumn::getProjectId, projectId)
                .eq(SurveyColumn::getCtype, ctype)
                .eq(SurveyColumn::getCol, meta)
                .ne(SurveyColumn::getStatus, COLUMN_STATUS_DELETE.getNumber());
        SurveyColumn surveyColumn = columnMapper.selectOne(queryWrapper);
        if (ObjectUtil.isNotEmpty(surveyColumn)) {
            columnMapper.update(null, new LambdaUpdateWrapper<SurveyColumn>()
                    .eq(SurveyColumn::getProjectId, projectId)
                    .eq(SurveyColumn::getCtype, ctype)
                    .eq(SurveyColumn::getCol, meta)
                    .ne(SurveyColumn::getStatus, COLUMN_STATUS_DELETE.getNumber())
                    .set(Objects.nonNull(status), SurveyColumn::getStatus, status)
                    .set(SurveyColumn::getSortNo, sort_no));
        } else {
            SurveyColumn column = new SurveyColumn();
            column.setProjectId(projectId);
            column.setCtype(ctype);
            column.setCol(meta);
            column.setSortNo(sort_no);
            column.setQstructIds("[]");
            column.setCustomAttr("{}");
            column.setQtype(COLUMN_QTYPE_QUESTION_VALUE);
            if (ObjectUtil.isNotEmpty(status)) {
                column.setStatus(status);
            } else {
                column.setStatus(COLUMN_STATUS_SHOW_VALUE);
            }
            columnMapper.insert(column);
        }

        sort_no += 1;
        return sort_no;
    }

    /**
     * 问题列，处理问卷所有版本列
     * 问卷发布需要执行所有题目的版本，因为要对题目对排序，重新设置sort_no
     *
     * @param project_id
     */
    @Override
    public void createQColumns(String project_id) {
        Project project = qdesGrpcClient.getProject(project_id);
        if (ObjectUtil.isNotEmpty(project) && project.getReleaseVer() <= 0) {
            return;
        }
        List<QStruct> qstruct_list = qdesGrpcClient.qdesListQstructWithq(project_id, project.getReleaseVer());
        if (ObjectUtil.isEmpty(qstruct_list)) {
            throw new BusinessException("cannot find qstruct");
        }
        Set<String> columns_set = new HashSet<>();
        QstructsDto qstructsDto = grpcConvertMapper.toQstructsDto(qstruct_list.get(0));
        if (ObjectUtil.isNotEmpty(qstructsDto.getData())) {
            int sort_no = 0;
            List<JSONObject> dataList = surveyCommonService.walkQstructQuestions(JSONObject.parseObject(qstructsDto.getData()));
            for (JSONObject question : dataList) {
                question.put("sort_no", sort_no);
                JSONObject parse_dict = QUESTION_PARSERS.get(question.getString("qtype")).apply(qstructsDto, question);
                columns_set.addAll(Optional.of(new HashSet<>(parse_dict.getJSONArray("columns_set").toJavaList(String.class))).orElse(new HashSet<>()));
                sort_no += 1000;
            }
        }
        //把其他版本的column status 设置成 COLUMN_STATUS_DELETE
        columnMapper.update(null, new LambdaUpdateWrapper<SurveyColumn>()
                .eq(SurveyColumn::getProjectId, project)
                .eq(SurveyColumn::getCtype, COLUMN_CTYPE_QUESTION.getNumber())
                .notIn(ObjectUtil.isNotEmpty(columns_set), SurveyColumn::getId, columns_set)
                .set(SurveyColumn::getStatus, COLUMN_STATUS_DELETE.getNumber()));
    }

    @Override
    public void surveyAddQuote(CreateQuotaDto dto) {
        log.info("surveyAddQuote------{}", JSON.toJSONString(dto));
        String project_id = dto.getProjectId();
        List<String> quota_checkitem_ids = dto.getQuotaCheckitemIds();
        if (ObjectUtils.isEmpty(project_id) || ObjectUtils.isEmpty(quota_checkitem_ids)) {
            return;
        }
        quota_checkitem_ids.stream().filter(StrUtil::isNotBlank).forEach(this::quoteNumSubmit);
    }

    @Override
    public void surveyPopQuote(CreateQuotaDto dto) {
        log.info("surveyPopQuote------{}", JSON.toJSONString(dto));
        String project_id = dto.getProjectId();
        List<String> quota_checkitem_ids = dto.getQuotaCheckitemIds();
        if (ObjectUtils.isEmpty(project_id) || ObjectUtils.isEmpty(quota_checkitem_ids)) {
            return;
        }
        quota_checkitem_ids.stream().filter(StrUtil::isNotBlank).forEach(check_items_id -> quoteNumChange(check_items_id, -1));
    }

    private void quoteNumSubmit(String check_items_id) {
        SurveyQuotaCheckItem quota_check_item = surveyQuotaCheckItemService.quoteCountAutoInc(check_items_id, 1);
        if (!ObjectUtils.isEmpty(quota_check_item) && Objects.equals(quota_check_item.getCurCount(), quota_check_item.getNum())) {
            TicketNotificationTriggerDto params = new TicketNotificationTriggerDto();
            params.setProjectId(quota_check_item.getProjectId());
            params.setTypeTag(quota_check_item.getId());
            //配额满提醒
            params.setNoticeType(5);
            params.setRspdCount(quota_check_item.getCurCount());
            surveyMessageSendService.notificationTriggerSend(params);
        }
    }

    private void quoteNumChange(String check_items_id, Integer step) {
        SurveyQuotaCheckItem quota_check_item = surveyQuotaCheckItemService.quoteCountAutoInc(check_items_id, step);
    }

    @Override
    public void surveyRespondentStatusChanged(JSONObject data) {
        //通知提醒：答卷数据状态变更相关提醒处理
        int seq = data.getIntValue("seq");
        if (seq == 0) {
            return;
        }

        String project_id = data.getString("project_id");
        SurveyRespondent rspd = surveyRespondentMapper.selectOne(Wrappers.<SurveyRespondent>lambdaQuery()
                .eq(SurveyRespondent::getSeq, seq)
                .eq(SurveyRespondent::getProjectId, project_id)
                .last("limit 1")
        );
        //没有rspd 证明数据未进入答卷数据列表。答卷数据状态为非正式数据，不需要统计
        if (Objects.isNull(rspd) || !Objects.equals(rspd.getDataStatus(), RESPONDENT_DATA_STATUS_FORMAL_VALUE)) {
            return;
        }

        Integer prev = data.getInteger("prev");
        Integer cur = data.getInteger("current");
        if (cur == null || prev == null) {
            return;
        }
        log.info("surveyRespondentStatusChanged------{}", JSON.toJSONString(data));

        if (Objects.equals(prev, RESPONDENT_STATUS_FINISH_VALUE) && !Objects.equals(cur, RESPONDENT_STATUS_FINISH_VALUE)) {
            respondentNumChange(project_id, -1);
        } else if (Objects.equals(cur, RESPONDENT_STATUS_FINISH_VALUE) && !Objects.equals(prev, RESPONDENT_STATUS_FINISH_VALUE)) {
            respondentNumChange(project_id, 1);
        } else {
            log.info("surveyRespondentStatusChanged do nothings");
        }
    }

    @Override
    public void respondentNumSubmit(JSONObject dto) {
        //async def rspd_notification_processor(data):
        //通知提醒：答卷数据相关提醒处理

        if (Objects.isNull(dto.get("data_status")) || !Objects.equals(dto.get("data_status"), RESPONDENT_DATA_STATUS_FORMAL_VALUE)) {
            return;
        }
        if (Objects.isNull(dto.get("status")) || !Objects.equals(dto.get("status"), RESPONDENT_STATUS_FINISH_VALUE)) {
            return;
        }
        log.info("通知提醒记录答卷数量------" + dto.toJSONString());

        String project_id = dto.getString("project_id");
        Long seq = dto.getLong("seq");
        boolean is_copy_demo = dto.getBooleanValue("is_copy");
        respondentNumSubmit(project_id, seq, is_copy_demo);
    }

    @Override
    public void createFootprint(JSONObject dto) {
        //@mq.subscribe(topics.member_created_by_respondent_submit)
        //async def create_footprint(data):
        //开启同步联系人，会根据题目中的手机号和邮箱新建或者更新联系人，
        //当创建或者更新联系人之后把答卷信息更新到联系人足迹表（Java数据库）中
        //java创建联系人更新答卷之后，会发过来答卷的seq，Python这边查到答卷更新联系人足迹

        Long seq = dto.getLong("seq");
        String project_id = dto.getString("project_id");
        log.info("****************同步联系人，答卷刷到联系人足迹点{}********start******", seq);
        if (seq == null || StrUtil.isBlankIfStr(project_id)) {
            log.error("联系人足迹同步：未收到从Java传递过来的答卷seq");
            return;
        }

        SurveyRespondent rspd = surveyRespondentMapper.selectOne(Wrappers.<SurveyRespondent>lambdaQuery()
                .eq(SurveyRespondent::getSeq, seq)
                .eq(SurveyRespondent::getProjectId, project_id)
                .last("limit 1")
        );
        if (Objects.isNull(rspd)) {
            log.error("答卷seq不存在");
            return;
        }

        SurveyRespondentData rspd_data = surveyRespondentDataMapper.selectOne(Wrappers.<SurveyRespondentData>lambdaQuery()
                .eq(SurveyRespondentData::getSeq, seq)
                .eq(SurveyRespondentData::getProjectId, project_id)
                .last("limit 1")
        );
        JSONObject total_answer = JSONObject.parseObject(rspd_data.getAnswers());
        JSONObject custom_answer = JSONObject.parseObject(rspd_data.getCustomAnswers());
        surveyCommonService.memberFootPrintMq(rspd, total_answer, custom_answer);
        log.info("****************同步联系人，答卷刷到联系人足迹点{}*********end*****", seq);
    }

    private void respondentNumSubmit(String project_id, Long seq, boolean is_demo) {
        if (is_demo) {
            return;
        }
        Map<String, Long> kwargs = new HashMap<>();
        kwargs.put("count_finish", 1L);
        JSONObject rspd_count = surveyCommonService.respondentCountAutoInc(project_id, kwargs);

        JSONObject params = new JSONObject();
        params.put("project_id", rspd_count.get("project_id"));
        params.put("n_type_tag", rspd_count.get("project_id"));
        params.put("count", rspd_count.get("count_finish"));
        params.put("seq", seq);

        //第一份提醒
        if (rspd_count.getIntValue("count_finish") == 1) {
            params.put("n_type", 0);
            //await redis_mq.publish(topics.ticket_notification_trigger, params)
            surveyMessageSendService.notificationTriggerSend(params.toJavaObject(TicketNotificationTriggerDto.class));
        }

        //每次提醒
        params.put("n_type", 1);
        //await redis_mq.publish(topics.ticket_notification_trigger, params)
        surveyMessageSendService.notificationTriggerSend(params.toJavaObject(TicketNotificationTriggerDto.class));

        //达到指定次数提醒
        params.put("n_type", 2);
        //await redis_mq.publish(topics.ticket_notification_trigger, params)
        surveyMessageSendService.notificationTriggerSend(params.toJavaObject(TicketNotificationTriggerDto.class));
    }

    private void respondentNumChange(String project_id, int step) {
        Map<String, Long> kwargs = new HashMap<>();
        kwargs.put("count_finish", (long) step);
        surveyCommonService.respondentCountAutoInc(project_id, kwargs);
    }

    /*
    后半部分元数据
     */
    private void createMetaColumnsBehind(String project_id) {
        List<SurveyColumn> behind_columns = columnMapper.selectList(new LambdaQueryWrapper<SurveyColumn>()
                .eq(SurveyColumn::getProjectId, project_id)
                .eq(SurveyColumn::getCtype, ColumnCtype.COLUMN_CTYPE_META_BEHIND.getNumber())
                .ne(SurveyColumn::getStatus, COLUMN_STATUS_DELETE.getNumber()));
        Map<String, Object> col_columns_map = new HashMap<>();
        if (behind_columns.size() > 0) {
            behind_columns.forEach(behind_column -> {
                col_columns_map.put(behind_column.getCol(), behind_column);
                if (!COLUMNS_META_BEHIND.contains(behind_column.getCol())) {
                    behind_column.setStatus(COLUMN_STATUS_DELETE.getNumber());
                    columnMapper.updateById(behind_column);
                }
            });
        }
        int sort_no = 0;
        List<String> metas = new ArrayList<>(COLUMNS_META_BEHIND);
        for (String meta : metas) {
            SurveyColumn surveyColumn = (SurveyColumn) col_columns_map.get(meta);
            if (col_columns_map.containsKey(meta) && surveyColumn.getStatus() != COLUMN_STATUS_DELETE.getNumber()) {
                surveyColumn.setSortNo(sort_no);
                sort_no += 1;
                columnMapper.updateById(surveyColumn);
            }
            if ((Lists.newArrayList("渠道", "用户名称", "用户所在国家")).contains(meta)) {
                sort_no = findOneAndUpdateColumns(project_id, COLUMN_CTYPE_META_BEHIND.getNumber(), meta, sort_no, null);
            } else {
                sort_no = findOneAndUpdateColumns(project_id, COLUMN_CTYPE_META_BEHIND.getNumber(), meta, sort_no, COLUMN_STATUS_HIDE.getNumber());
            }
        }
    }

    private boolean checkSurveyExistsScore(String id, int version) {
        List<QstructsDto> qstructsDtos = qdesGrpcClient.listQStruct(id, version);
        for (QstructsDto s : qstructsDtos) {
            //{"2":[{"o_gid":"3","r_gid":"5","score":"10"},{"o_gid":"3","r_gid":"6","score":"30"}]}
            JSONObject scores = JSONObject.parseObject(s.getScores());
            if (ObjectUtils.isEmpty(scores)) {
                return false;
            }

            JSONObject data = JSONObject.parseObject(s.getData());
            Set<String> exists_gid = surveyColumnService.getQstructItemsGids(data);

            if (ObjectUtils.isEmpty(scores)) {
                return false;
            }

            for (String key : scores.keySet()) {
                if (exists_gid.contains(key)) {
                    JSONArray jsonArray = scores.getJSONArray(key);
                    for (int i = 0; i < jsonArray.size(); i++) {
                        JSONObject item = jsonArray.getJSONObject(i);
                        if (item.getIntValue("score") > 0) {
                            return true;
                        }
                    }
                }
            }
        }
        return false;
    }

    private void createScoreColumns(String project_id) {
        Project project = qdesGrpcClient.getProject(project_id);
        if (ObjectUtil.isNotEmpty(project) && project.getReleaseVer() <= 0) {
            return;
        }
        //判断是否有总分字段
        boolean exists_score = checkSurveyExistsScore(project_id, project.getReleaseVer());
        //已存在总分字段不需要处理
        SurveyColumn surveyColumn = columnMapper.selectOne(new LambdaQueryWrapper<SurveyColumn>()
                .eq(SurveyColumn::getProjectId, project_id)
                .eq(SurveyColumn::getCtype, COLUMN_CTYPE_SCORE.getNumber()));
        if (ObjectUtil.isNotEmpty(surveyColumn)) {
            Integer status = surveyColumn.getStatus();
            if (exists_score) {
                if (status == COLUMN_STATUS_DELETE.getNumber()) {
                    //存在总分字段, 显示总分字段
                    surveyColumn.setStatus(COLUMN_STATUS_HIDE.getNumber());
                    columnMapper.updateById(surveyColumn);
                }
            } else {
                if (status != COLUMN_STATUS_DELETE.getNumber()) {
                    //存在总分字段, 显示总分字段
                    surveyColumn.setStatus(COLUMN_STATUS_DELETE.getNumber());
                    columnMapper.updateById(surveyColumn);
                }
            }
        } else {
            if (exists_score) {
                //不存在总分字段创建
                SurveyColumn column = new SurveyColumn();
                column.setCol("总分");
                column.setProjectId(project_id);
                column.setCtype(COLUMN_CTYPE_SCORE.getNumber());
                column.setSortNo(1);
                column.setQstructIds("[]");
                column.setCustomAttr("{}");
                column.setQtype(COLUMN_QTYPE_QUESTION_VALUE);
                column.setStatus(COLUMN_STATUS_HIDE.getNumber());
                columnMapper.insert(column);
            }
        }
    }
}
